/*
 * Painting.java	1.0  01/02/2006
 * Painting.java	2.0  10/02/2008
 *
 * This class contains the OpenGL component that  
 * receives, calls, and processes OpenGL functions 
 * on the painting image created
 * Author:    Isabelle Bichindaritz
 *
 */

import java.io.*;
import java.nio.*;
import java.awt.image.*;
import java.awt.*;
import java.awt.event.*;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;

public class Painting implements GLEventListener, MouseListener, MouseMotionListener {

    private GLAutoDrawable GLAutoDrawable;			// the drawable where OpenGL operates
    private byte[] rgba;				// raster
    private GLJPanel panel;				// copy of the panel that displays this painting
    private String eventType = "";			// event type from mouse listener
    private MouseEvent event;			// event sent by mouse listener
    int height, width; 				// eight and width of the painted image
    private int first, clearButton, paintButton = 0;
    private Brush brush;
    private Point source, target, move;
    private int offset = 37;

    public Painting(GLJPanel p) {
        // set the panel to 'p'
        panel = p;
    }

    public void init(GLAutoDrawable drawable) // intialization of the drawable
    {
        // System.out.println("Painting init");
        final GL gl = drawable.getGL();
        final GLU glu = new GLU();
        //System.out.println("First gl=" + gl);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluOrtho2D(0.0, width, 0.0, height);   // erases other objects
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glClearColor(0.7f, 0.7f, 0.7f, 1.0f);

        //       System.out.println("Init GL is " + gl.getClass().getName());
        //System.out.println("Second gl=" + gl);
    }

    public void display(GLAutoDrawable drawable) // display called whenever something needs to be drawn on the drawable
    // if necessary, this method is invoked from display() whenever we
    // want to refresh the display
    {
        // System.out.println("Painting display, first = " + first);
        // initializations
        if (GLAutoDrawable == null) {
            GLAutoDrawable = drawable;
        }
        ;
        final GL gl = drawable.getGL();
        final GLU glu = new GLU();
        int frameHeight = height;
        frameHeight = Painter.frameHeight;
        // the first time, clear the screen
        if (first == 0) {
            gl.glClear(GL.GL_COLOR_BUFFER_BIT);
            first = 1;
        }

        // update the display from the Painter buttons and the Picture mouse
        // interaction, setting the pixel selected
        // by the mouse to the color from the original picture
        if (clearButton == 1) // clear the Painting display
        {
            clearPainting(drawable);
            clearButton = 0;
        } else if (paintButton == 1) // paint the complete display
        {
            paintWholeScreen(drawable);
            paintButton = 0;
        } else if ((eventType.equals("mouse pressed")) && (event.getY() < height)
                && (event.getY() >= 0)) {
            source = new Point(event.getX(), height - offset - event.getY());
            target = new Point(event.getX(), height - offset - event.getY());
            gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
            gl.glDisable(GL.GL_DEPTH_TEST);
            gl.glEnable(GL.GL_BLEND);
            gl.glRasterPos2i(0, 0);
            // initiate the brush painting
            if (Painter.currentBrush.equals("Point")) {
                brush = new PointBrush();
                ((PointBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Circle")) {
                brush = new CircleBrush();
                ((CircleBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Scribble")) {
                brush = new ScribbleBrush();
                ((ScribbleBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Dotted Line")) {
                brush = new DotBrush();
                ((DotBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Spray Can")) {
                brush = new SprayCan();
                ((SprayCan) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Comb")) {
                brush = new CombBrush();
                ((CombBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            }
        } else if ((eventType.equals("mouse dragged"))
                && (event.getY() < height)
                && (event.getY() >= 0)) {
            // carry out the brush painting
            if (Painter.currentBrush.equals("Point")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((PointBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Circle")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((CircleBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Scribble")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((ScribbleBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Dotted Line")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((DotBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Spray Can")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((SprayCan) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Comb")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((CombBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            }
        } else if (eventType.equals("mouse released")) {
            // stop the brush painting
            if (Painter.currentBrush.equals("Point")) {
                ((PointBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Circle")) {
                ((CircleBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Scribble")) {
                ((ScribbleBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Dotted Line")) {
                ((DotBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Spray Can")) {
                ((SprayCan) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Comb")) {
                ((CombBrush) brush).brushEnd(source, target);
                eventType = "";
            }
        }
        // update the display from the Painter buttons and the Picture mouse
        // interaction, setting the pixel selected
        // by the mouse to the color from the original picture
        if (clearButton == 1) // clear the Painting display
        {
            clearPainting(drawable);
            clearButton = 0;
        } else if (paintButton == 1) // paint the complete display
        {
            paintWholeScreen(drawable);
            paintButton = 0;
        } else if ((eventType.equals("mouse pressed")) && (event.getY() < height)
                && (event.getY() >= 0)) {
            source = new Point(event.getX(), height - offset - event.getY());
            target = new Point(event.getX(), height - offset - event.getY());
            gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
            gl.glDisable(GL.GL_DEPTH_TEST);
            gl.glEnable(GL.GL_BLEND);
            gl.glRasterPos2i(0, 0);
            // initiate the brush painting
            if (Painter.currentBrush.equals("Point")) {
                brush = new PointBrush();
                ((PointBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Circle")) {
                brush = new CircleBrush();
                ((CircleBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Scribble")) {
                brush = new ScribbleBrush();
                ((ScribbleBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Dotted Line")) {
                brush = new DotBrush();
                ((DotBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Spray Can")) {
                brush = new SprayCan();
                ((SprayCan) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Comb")) {
                brush = new ScribbleBrush();
                ((CombBrush) brush).brushBegin(source, target, GLAutoDrawable);
                eventType = "";
            }
        } else if ((eventType.equals("mouse dragged"))
                && (event.getY() < height)
                && (event.getY() >= 0)) {
            // carry out the brush painting
            if (Painter.currentBrush.equals("Point")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((PointBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Circle")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((CircleBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Scribble")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((ScribbleBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Dotted Line")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((DotBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Spray Can")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((SprayCan) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            } else if (Painter.currentBrush.equals("Comb")) {
                move = new Point(event.getX(), height - offset - event.getY());
                target.setLocation(event.getX(), height - offset - event.getY());
                ((CombBrush) brush).brushMove(move, target, GLAutoDrawable);
                eventType = "";
            }
        } else if (eventType.equals("mouse released")) {
            // stop the brush painting
            if (Painter.currentBrush.equals("Point")) {
                ((PointBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Circle")) {
                ((CircleBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Scribble")) {
                ((ScribbleBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Dotted Line")) {
                ((DotBrush) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Spray Can")) {
                ((SprayCan) brush).brushEnd(source, target);
                eventType = "";
            } else if (Painter.currentBrush.equals("Comb")) {
                ((CombBrush) brush).brushEnd(source, target);
                eventType = "";
            }
        }
        gl.glFlush();
    }

    public void clearPainting() {
        // answers the Painter class 'clear' button by setting the 'clearButton' to 1
        // System.out.println ("Painting clearPainting()");
        clearButton = 1;
    }

    public void clearPainting(GLAutoDrawable drawable) // answers the Painter class 'clear' button by reinitializing the raster to white
    {
        // System.out.println ("Painting clearPainting(drawable)");
        rgba = new byte[width * height * 4];
        for (int k = 0; k < rgba.length; k++) {
            rgba[k] = (byte) Color.white.getRed();
            rgba[k + 1] = (byte) Color.white.getGreen();
            rgba[k + 2] = (byte) Color.white.getBlue();
            rgba[k + 3] = (byte) Color.white.getAlpha();
            k = k + 3;
        }
        ;
        displayImageRGBA(drawable);   // refresh display with raster
    }

    public void paintWholeScreen() {
        // answers the Painter class 'paint' button by setting the 'paintButton' to 1
        // System.out.println ("Painting paintWholeScreen()");
        paintButton = 1;
    }

    public void paintWholeScreen(GLAutoDrawable drawable) {
        // answers the Painter class 'paint' button by copying the original picture in its entirety
        // System.out.println ("Painting paintWholeScreen(drawable)");
        Brush brush;
        Point source, target, move;

        final GL gl = drawable.getGL();
        final GLU glu = new GLU();
        source = new Point(0, 0);
        target = new Point(0, 0);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        gl.glDisable(GL.GL_DEPTH_TEST);
        gl.glEnable(GL.GL_BLEND);
        gl.glRasterPos2i(0, 0);
        if (Painter.currentBrush.equals("Point")) {
            brush = new PointBrush();
            // initiate the brush action
            ((PointBrush) brush).brushBegin(source, target, drawable);
            move = new Point(0, 0);
            for (int k = 0; k < height; k++) // y < height
            {
                for (int l = 0; l < width; l++) // x < width
                {
                    move.setLocation(l, k);
                    target.setLocation(l, k);
                    // continue the brush action
                    ((PointBrush) brush).brushMove(move, target, drawable);

                    l = l + (Painter.size - 1);
                }
                ;
                k = k + (Painter.size - 1);
            }
            // end the brush action
            ((PointBrush) brush).brushEnd(source, target);

        } else if (Painter.currentBrush.equals("Circle")) {
            brush = new CircleBrush();
            // initiate the brush action
            ((CircleBrush) brush).brushBegin(source, target, drawable);
            move = new Point(0, 0);
            for (int k = 0; k < height; k++) // y < height
            {
                for (int l = 0; l < width; l++) // x < width
                {
                    move.setLocation(l, k);
                    target.setLocation(l, k);
                    // continue the brush action
                    ((CircleBrush) brush).brushMove(move, target, drawable);

                    l = l + (Painter.size - 1);
                }
                k = k + (Painter.size - 1);
            }
            // end the brush action
            ((CircleBrush) brush).brushEnd(source, target);

        } else if (Painter.currentBrush.equals("Scribble")) {
            brush = new ScribbleBrush();
            // initiate the brush action
            ((ScribbleBrush) brush).brushBegin(source, target, drawable);
            move = new Point(0, 0);
            for (int k = 0; k < height; k++) // y < height
            {
                for (int l = 0; l < width; l++) // x < width
                {
                    move.setLocation(l, k);
                    target.setLocation(l, k);
                    // continue the brush action
                    ((ScribbleBrush) brush).brushMove(move, target, drawable);

                    l = l + (Painter.size - 1);
                }
                k = k + (Painter.size - 1);
            }
            // end the brush action
            ((ScribbleBrush) brush).brushEnd(source, target);

        } else if (Painter.currentBrush.equals("Dotted Line")) {
            brush = new DotBrush();
            // initiate the brush action
            ((DotBrush) brush).brushBegin(source, target, drawable);
            move = new Point(0, 0);
            for (int k = 0; k < height; k++) // y < height
            {
                for (int l = 0; l < width; l++) // x < width
                {
                    move.setLocation(l, k);
                    target.setLocation(l, k);
                    // continue the brush action
                    ((DotBrush) brush).brushMove(move, target, drawable);

                    l = l + (Painter.size - 1);
                }
                k = k + (Painter.size - 1);
            }
            // end the brush action
            ((DotBrush) brush).brushEnd(source, target);

        } else if (Painter.currentBrush.equals("Spray Can")) {
            brush = new SprayCan();
            // initiate the brush action
            ((SprayCan) brush).brushBegin(source, target, drawable);
            move = new Point(0, 0);
            for (int k = 0; k < height; k++) // y < height
            {
                for (int l = 0; l < width; l++) // x < width
                {
                    move.setLocation(l, k);
                    target.setLocation(l, k);
                    // continue the brush action
                    ((SprayCan) brush).brushMove(move, target, drawable);

                    l = l + (Painter.size - 1);
                }
                k = k + (Painter.size - 1);
            }
            // end the brush action
            ((SprayCan) brush).brushEnd(source, target);

        } else if (Painter.currentBrush.equals("Comb")) {
            brush = new CombBrush();
            // initiate the brush action
            ((CombBrush) brush).brushBegin(source, target, drawable);
            move = new Point(0, 0);
            for (int k = 0; k < height; k++) // y < height
            {
                for (int l = 0; l < width; l++) // x < width
                {
                    move.setLocation(l, k);
                    target.setLocation(l, k);
                    // continue the brush action
                    ((CombBrush) brush).brushMove(move, target, drawable);

                    l = l + (Painter.size - 1);
                }
                k = k + (Painter.size - 1);
            }
            // end the brush action
            ((CombBrush) brush).brushEnd(source, target);

        }
    }

    private void showRGBA() {
        // used for testing the different buffers
        int s = 0;
        for (int i = 0; i < height; i++) // y
        {
            for (int j = 0; j < width; j++) // x
            {
                if (((j > 380) && (i == height - 2)) || ((j < 50) && (i == height - 1))) {
                    // System.out.println("Point (" + j + "," + i + ")");
                    // System.out.println("Before Picture  " + Painter.imageRGBA[s] + " "
                    //				+ Painter.imageRGBA[s+1] + " " + Painter.imageRGBA[s+2]);
                    // System.out.println("Before Painting " + rgba[s] + " " + rgba[s+1] + " " + rgba[s+2]);
                    // System.out.println("After Painting " + Painter.getOriginalPixel(new Point(j,i))[0] + " "
                    //						    + Painter.getOriginalPixel(new Point (j,i))[1] + " "
                    //						    + Painter.getOriginalPixel(new Point (j,i))[2] + " ");
                }
                s = s + 4;
            }
        }
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) // redraws the drawable with this new size
    {

        //System.out.println("Painting reshape width=" + width + "  height=" + height);

        final GL gl = drawable.getGL();
        final GLU glu = new GLU();
        gl.glViewport(0, 0, width, height);
    }

    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) // called whenever the display is changed
    {
        // System.out.println("Painting displayChanged");
    }

    public void setImage(byte[] img, int w, int h) {
        // initializes the image, its width, and its height
        // System.out.println("Painting setImage");

        rgba = img;
        width = w;
        height = h;
    }

    private void displayTriangle(GLAutoDrawable drawable) {
        // used for test purposes
        // System.out.println("drawable=" + drawable);

        final GL gl = drawable.getGL();
        final GLU glu = new GLU();
        // System.out.println("Twentieth gl=" + gl);
        gl.glColor3f(1.0f, 0.0f, 0.0f);
        gl.glPushMatrix();
        gl.glBegin(GL.GL_TRIANGLES);
        gl.glVertex2i(100, 50);
        gl.glVertex2i(100, 100);
        gl.glVertex2i(150, 75);
        gl.glEnd();
        gl.glPopMatrix();
    }

    protected void displayImageRGBA(GLAutoDrawable drawable) {
        // displays the picture from its raster on the drawable
        // System.out.println( "Painting displayRGBA");

        if (rgba != null) {
            final GL gl = drawable.getGL();
            final GLU glu = new GLU();
            gl.glClear(GL.GL_COLOR_BUFFER_BIT);
            // enable alpha mask (import from gif sets alpha bits)
            gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
            gl.glEnable(GL.GL_BLEND);


            // draw bytes into OpenGL
            gl.glRasterPos2i(0, 0);
            // System.out.println ("Image, width=" +
            //                            width + ", height=" + height + ", rgba.length=" + rgba.length);
            gl.glDrawPixels(width, height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, ByteBuffer.wrap(rgba));
            gl.glFlush();
        }

    }

    public void mousePressed(MouseEvent e) // processes mouse pressed event
    {
        //System.out.println("mouse pressed");
        event = e;
        eventType = "mouse pressed";

        Painter.picture.setEventType("mouse moved", e);
        Painter.picturePanel.display();

        panel.display();
    }

    public void mouseReleased(MouseEvent e) // processes mouse released event
    {
        //System.out.println("mouse released");
        event = e;
        eventType = "mouse exited";

        Painter.picture.setEventType("mouse moved", e);
        Painter.picturePanel.display();

        panel.display();
    }

    public void mouseDragged(MouseEvent e) // processes mouse dragged event
    {
        //System.out.println("mouse dragged");
        event = e;
        eventType = "mouse dragged";

        Painter.picture.setEventType("mouse moved", e);
        Painter.picturePanel.display();

        panel.display();
    }

    public void mouseExited(MouseEvent e) // processes mouse exited event
    {
        //System.out.println("mouse exited");
        event = e;
        eventType = "mouse exited";
        Painter.picture.setEventType("mouse exited", e);
        Painter.picturePanel.display();
    }

    public void mouseEntered(MouseEvent e) // processes mouse entered event
    {
        //System.out.println("mouse entered");
        event = e;
        eventType = "mouse entered";

        Painter.picture.setEventType("mouse entered", e);
        Painter.picturePanel.display();
    }

    public void mouseClicked(MouseEvent e) // processes mouse clicked event
    {
        event = e;
        eventType = "mouse clicked";

        Painter.picture.setEventType("mouse clicked", e);
        Painter.picturePanel.display();
    }

    public void mouseMoved(MouseEvent e) // processes mouse moved event
    {
        //System.out.println("mouse moved");
        event = e;
        eventType = "mouse moved";

        Painter.picture.setEventType("mouse moved", e);
        Painter.picturePanel.display();
    }
} // end Painting class

